#include "pch.h"
#include "FrameBuffer.h"

FrameBuffer::FrameBuffer()
{
    for (int i = 0; i < 3; ++i) {
        _buffers[i] = nullptr;
    }
}

FrameBuffer::~FrameBuffer()
{
    for (int i = 0; i < 3; ++i) {
        if (_buffers[i])
        {
            av_freep(&_buffers[i]);
        }
    }
}

void FrameBuffer::resize(int width, int height, int len)
{
    _updated.store(false, std::memory_order_release);
    _currentBuffer = 0;
    _lastRetrievedBuffer = -1;
    _width = width;
    _height = height;
    _len = len;
    if (_lenMax<_len)
    {
        _lenMax = _len;
        for (int i = 0; i < 2; ++i) {

            _buffers[i] = (uint8_t*)av_realloc(_buffers[i], _len);
        }
    } 
    _resized.store(true, std::memory_order_release);
    _moved.store(true, std::memory_order_release);
}

uint8_t* FrameBuffer::getCurrentBuffer()
{
    return _buffers[_currentBuffer];
}

void FrameBuffer::swapBuffers()
{
    if (_moved.exchange(false, std::memory_order_acquire))
    {
        _lastRetrievedBuffer = _currentBuffer;
        _currentBuffer = 1 - _currentBuffer;
        _updated.store(true, std::memory_order_release);
    }
    _condVar.notify_one();
}

int FrameBuffer::getAvailableBuffer(char** data, int* len, int* w, int* h, int block)
{
    if(block)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        _condVar.wait(lock, [this] { return _updated.load(std::memory_order_acquire) || _resized.load(std::memory_order_acquire); });
    }

    if (_resized.exchange(false, std::memory_order_acquire))
    {
        _buffers[2] = (uint8_t*)av_realloc(_buffers[2], _lenMax);
    }

    if (_updated.exchange(false, std::memory_order_acquire))
    {        
        uint8_t* temp = _buffers[_lastRetrievedBuffer];
        _buffers[_lastRetrievedBuffer] = _buffers[2];
        _buffers[2] = temp;
        _moved.store(true, std::memory_order_release);
    }
    else
    {
        return -3;
    }

    *data = (char*)_buffers[2];
    *len = _len;
    *w = _width;
    *h = _height;
    return 0;
}

void FrameBuffer::activateAll()
{
    _resized.store(true, std::memory_order_release);
    _updated.store(false, std::memory_order_release);
    _moved.store(true, std::memory_order_release);
    _condVar.notify_all();
}